package edu.siu.math.egut.main;

import java.io.IOException;
import java.util.regex.Matcher;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.io.ParabolicException;
import edu.siu.math.egut.io.ParabolicPieceException;
import edu.siu.math.egut.io.SunipotentFormatException;
import edu.siu.math.egut.util.BadSubstitutionException;
import edu.siu.math.egut.util.Group;
import edu.siu.math.egut.util.Matrix;
import edu.siu.math.egut.util.MatrixMultiplicationException;
import edu.siu.math.egut.util.NoSuchElementException;
import edu.siu.math.egut.util.NotARootException;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Sunipotent;
import edu.siu.math.egut.util.Unipotent;

/**
 * @author Joseph Hundley
 *
 */
public class CharacterAction {

    private static final String INSTRUCTIONS = "This program requires at least five arguments:\n" +
    	    		"(1) the name of an exceptional group\n" +
    	    		"(2) a proper parabolic subgroup, specified by naming the simple roots in the unipotent radical\n" +
    	    		"(3) a piece of that unipotent radical, specified either by\n" +
    	    		"\t(a) giving an integral weight for each root named at step (2), or\n" +
    	    		"\t(b) simply giving a single integer,\n" +
    	    		"(4) a polynomial function on the corresponding unipotent subgroup\n" +
    	    		"(5) at least one simple group element to act." +
    	    		"EXAMPLE: F4 4 2 u1232 x_{0110}(u) x_{-0010}(-t^2)";

    /**
     * Let P =MU be a parabolic subgroup of a reductive group G.  Let {i1 < i2 < ... < ik}.
     * Let r be the semisimple rank of G and identify roots of G with r-tuples of integers 
     * by writing them in terms of the basis of simple roots.  Define a mapping Z^r -> Z^k by 
     * (n[1], ... n[r]) ->( n[i1], ..., n[ik]).  
     * Each of the fibers of this map defines an M-module.  
     * A realization of G determines a basis for each of these M-modules.  The purpose of this 
     * program is to compute the matrices of elements of M with respect to these bases.
     * 
     * Syntax:  CharAct Group Parabolic PieceSpecifier Character Elements
     * Example: CharAct F4 4 2 u1232 x_{0110}(r) [2,3] x_{-0010}(s) 
     * FIXME: results for Weyl elements are not currently correct.  
     * the reason is that change of variables algorithm doesn't change 
     * the variable on a Sunipotent when the root changes....
     * @param args need to specify the group G, parabolic P, and which piece we consider, and finally an element of M.
     */
    public static void main( String[] args) {
	
	if( args.length == 1){
	    args = Algorithms.getInput(args);
	}
	
	if( args.length < 5){
	    System.out.println(INSTRUCTIONS);
	    System.exit(1);
	}
	
	/* TODO introduce tweaks for easier input-output.
	 * 
	 * TODO  something odd is causing this program and DualPieceRep to produce 
	 * inconsistent results... and causing this program to disagree with itself 
	 * under some circumstances...
	 * 
	 * 
	 */
	
	// extract input from args
	// args[0] is supposed to be the name of a group.  Check that it is one. 
	final Group g = Group.getGroup(args[0]);
	if(g == null){
	    System.out.println("First argument --"+ args[0] + "-- is not the name of an exceptional group.");
	    System.exit(1);
	}
	try {
		/* args[1] should specify a parabolic subgroup of that exceptional group.  therefore, 
		 *   (1) every character in it should be a digit between 1 and the rank of g inclusive, and 
		 *   (2) they should all be different.
		 *   Those conditions being satisfied, the interpretation is that you are listing those simple roots
		 *   such that the corresponding root subgroup is in the unipotent radical.
		 */

	    
	    final String parabolicSpecifier = parseParabolic(args[1],g);
	    
	    /* args[2] should specify a piece.    Can do it in one of two ways 
	     * (a) an integer for each root listed in args[1]
	     * (b) a single integer.
	     * 
	     */
	    
	    final String pieceSpecifier = parsePiece(args[2], args[1].length());
	    
	    Polynomial ell = Polynomial.read(args[3]);
	    
	    /*
	     * get list of roots in the specified "piece group" from LiE
	     */
	    final LiE lie = new LiE(  );
	    final int[][] pieceRoots = Algorithms.readLiEMatrix(lie.exec("pieceGroup("+pieceSpecifier+","+parabolicSpecifier+","+args[0]+")", OutputPref.HIDE));
	    

	    // set up unipotent u
	    final Unipotent u = new Unipotent( g, pieceRoots, "u");
	    
	    
	    // treat remaining args as specifying group elements by which to act
	    for( int i = args.length - 1; i >3 ; i-- ){  //  we start with the last:  this is a (left) action, so the rightmost group element acts first..
		try {
		    if(edu.siu.math.egut.util.Algorithms.representsWeylWord(edu.siu.math.egut.io.Algorithms.removeWhiteSpaceCommasAndBrackets(args[i]), g.getRank()))
			System.out.println("Weyl elements not currently well supported.");
		    u.actBy(args[i]);
		    ell = u.chOfVars(ell);

		} catch (final NoSuchElementException e) {
		    System.out.println("Argument "+i+"-- "+args[i]+"-- could not be read as a group element.  Ignored.");
		} catch (BadSubstitutionException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
		
	    }
	    
	    // make changes of variable, keeping track of the effect on ell
	    
	    // FIXME current change of variables algorithm does not change variable names to match roots.  So this will not 
	    // produce informative results.
	    
	    
	    // output result to user.
	    System.out.println(ell);
	    
	    
	    
	    
	} catch (final ParabolicException e) {
	    // TODO Auto-generated catch block
	    System.out.println("Second argument -- "+args[1] +"-- could not be parsed as a parabolic in "+args[0]+".\n" +
	    		"required format is to list the simple roots that are in the unipotent radical,\n" +
	    		"all mashed together.  Example: 134");
	    e.printStackTrace();
	    
	} catch (final ParabolicPieceException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final IOException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final PolynomialReadException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (final NotARootException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
//	} catch (NoSuchElementException e) {
//	    // TODO Auto-generated catch block
//	    e.printStackTrace();
//	} catch (SunipotentFormatException e) {
//	    // TODO Auto-generated catch block
//	    e.printStackTrace();
//	} catch (MatrixMultiplicationException e) {
//	    // TODO Auto-generated catch block
//	    e.printStackTrace();
	}
	
	
	
    
    }

    private static Matrix matrix(final String string, final int[][] vRoots, final Group g) throws PolynomialReadException, NotARootException, NoSuchElementException, SunipotentFormatException, MatrixMultiplicationException {
	/* For now, this method should check whether the string specifies a Sunipotent, and if 
	 * not, check whether it specifies a Weyl element, 
	 * and if not, throw an exception.
	 */
	Matcher s = Algorithms.suniString(g).matcher(string);
	if(s.matches()){
	    final Sunipotent suni = Sunipotent.read(string,g);
	    return Matrix.matrixOf(suni, vRoots, g);
	    
	    
	}
	
	s=Algorithms.weylWord(g).matcher(string);
	
	if( s.matches()){
	    return Matrix.matrixOfWeylWord(string, vRoots, g);
	
	}
	throw new NoSuchElementException();
    }

    /*
     * Idea here is to check the format of a string which is supposed to specify a piece, 
     * and then parse it from a convenient-to-type format into a format for LiE.
     * 
     */
    private static String parsePiece(final String string, final int ParabolicLength) throws ParabolicPieceException {
	if((string.length() != 1)&&(string.length() != ParabolicLength) )
	    throw new ParabolicPieceException();
	final StringBuilder sb = new StringBuilder("[");
	for( int i =0; i < string.length(); i++)
	{
	    if(string.charAt(i)<48||string.charAt(i)>57){
		throw new ParabolicPieceException(  );
	    }
	    sb.append(string.charAt(i));
	    sb.append(i==string.length()-1?"]":",");
	}
	
	return sb.toString();

    
    }

    /*The purpose of this method is twofold:  (1) to validate user input, and (2) to allow the main program to 
     * accept input in the easiest-for-the-user format, yet have the string in the correct format for 
     * feeding to LiE.  
     * 
     */
    private static String parseParabolic(final String string, final Group g) throws ParabolicException {
	
	final StringBuilder sb = new StringBuilder("[");
	
	for( int i =0; i < string.length(); i++)
	{
	    if(string.charAt(i)<49||string.charAt(i)>48+g.getRank()){
		throw new ParabolicException(  );
	    }
	    sb.append(string.charAt(i));
	    sb.append(i==string.length()-1?"]":",");
	}
	
	return sb.toString();
    }

}
